//
// Created by wxd on 2025/7/15.
//

#include "Texture.h"
#include "../glFramework/utils/ResourceLoadUtils.h"
#include "../wrapper/log/Logger.h"
#include <sstream>
#include <iostream>

const char* PACKAGE_NAME = "glFramework/texture";

Texture::~Texture() {
    if (m_TextureId) {
        glDeleteTextures(1, &m_TextureId);

        std::ostringstream oss;
        oss << "~Texture(): " << m_TextureId << "\n";
        LOG_DEBUG_METHOD(PACKAGE_NAME, oss.str());
    }
}

Texture::Texture(const std::string &texturePath, bool useTest) {
    if (useTest) {
        m_TextureId = 0,
        m_TextureUnit = 0,
        m_TextureType = TextureType::TEXTURE_2D,
        m_Width = 0, m_Height = 0, m_Channels = 0,
        m_InternalFormat = GL_RGBA, m_Format = GL_RGBA,
        m_DataType = GL_UNSIGNED_BYTE,
        m_MinFilter = FilterMode::LINEAR_MIPMAP_LINEAR, // 注意：如果使用的mipmap，则物体小于纹理的时候，这里的min-filter需要采用mipmap类型的
        m_MagFilter = FilterMode::LINEAR,
        m_WrapS = WrapMode::REPEAT,
        m_WrapT = WrapMode::REPEAT,
        m_WrapR = WrapMode::REPEAT,
        m_MipmapsGenerated = false;

        stbi_set_flip_vertically_on_load(true);
        ImageParam imageParam = ResourceLoadUtils::getInstance().loadImage(texturePath);
        m_Width = imageParam.width;
        m_Height = imageParam.height;
        m_Channels = imageParam.channels;
        determineFormat(m_Channels);

        glGenTextures(1, &m_TextureId);
        glActiveTexture(GL_TEXTURE0); // 默认激活0号纹理单元
        glBindTexture(GL_TEXTURE_2D, m_TextureId);
        /*glTexImage2D(GL_TEXTURE_2D, 0, m_InternalFormat, m_Width, m_Height, 0,
                        m_Format, m_DataType, imageParam.data);*/

        // 生成不同级别的纹理
        int width = m_Width;
        int height = m_Height;
        for (int level = 0; true; level++) {
            glTexImage2D(GL_TEXTURE_2D, level, m_InternalFormat, width, height, 0,
                        m_Format, m_DataType, imageParam.data);

            if (width == 1 && height == 1) break;

            width = width > 1 ? width / 2 : 1;
            height = height > 1 ? height / 2 : 1;
        }

        setTextureParameters();

        imageParam.release();
    }
}


// 创建默认参数：过滤参数，包裹参数，默认激活0号纹理单元，默认是2D纹理，多用于测试阶段
Texture::Texture(const std::string &texturePath) {
    m_TextureId = 0;
    m_TextureUnit = 0;
    m_TextureType = TextureType::TEXTURE_2D;
    m_Width = 0, m_Height = 0, m_Channels = 0;
    m_InternalFormat = GL_RGBA, m_Format = GL_RGBA;
    m_DataType = GL_UNSIGNED_BYTE;
    m_MinFilter = FilterMode::NEAREST;
    m_MagFilter = FilterMode::LINEAR;
    m_WrapS = WrapMode::REPEAT;
    m_WrapT = WrapMode::REPEAT;
    m_WrapR = WrapMode::REPEAT;
    m_MipmapsGenerated = false;

    stbi_set_flip_vertically_on_load(true);
    ImageParam imageParam = ResourceLoadUtils::getInstance().loadImage(texturePath);
    m_Width = imageParam.width;
    m_Height = imageParam.height;
    m_Channels = imageParam.channels;
    determineFormat(m_Channels);

    glGenTextures(1, &m_TextureId); // TODO-wxd: 空指针问题
    glActiveTexture(GL_TEXTURE0); // 默认激活0号纹理单元
    glBindTexture(GL_TEXTURE_2D, m_TextureId);
    glTexImage2D(GL_TEXTURE_2D, 0, m_InternalFormat, m_Width, m_Height, 0,
                    m_Format, m_DataType, imageParam.data);

    setTextureParameters();

    imageParam.release();
}

Texture::Texture(const std::string &texturePath, int textureUnit) {
    m_TextureId = 0,
    m_TextureUnit = textureUnit,
    m_TextureType = TextureType::TEXTURE_2D,
    m_Width = 0, m_Height = 0, m_Channels = 0,
    m_InternalFormat = GL_RGBA, m_Format = GL_RGBA,
    m_DataType = GL_UNSIGNED_BYTE,
    m_MinFilter = FilterMode::NEAREST,
    m_MagFilter = FilterMode::LINEAR,
    m_WrapS = WrapMode::REPEAT,
    m_WrapT = WrapMode::REPEAT,
    m_WrapR = WrapMode::REPEAT,
    m_MipmapsGenerated = false;

    ImageParam imageParam = ResourceLoadUtils::getInstance().loadImage(texturePath);
    m_Width = imageParam.width;
    m_Height = imageParam.height;
    m_Channels = imageParam.channels;
    determineFormat(m_Channels);

    glGenTextures(1, &m_TextureId);
    // 创建纹理的时候不要直接激活纹理，避免当前的状态机会影响后续的渲染状态
    // glActiveTexture(GL_TEXTURE0 + m_TextureUnit);
    glBindTexture(GL_TEXTURE_2D, m_TextureId); // 这里是临时绑定，仅将纹理参数设置到当前的纹理对象中
    glTexImage2D(GL_TEXTURE_2D, 0, m_InternalFormat, m_Width, m_Height, 0,
                    m_Format, m_DataType, imageParam.data);

    setTextureParameters();
}

// 外部传入纹理的filter + wrapper参数
Texture::Texture(const std::string &texturePath, const TextureParams& params) {
    m_TextureId = 0,
    m_TextureUnit = 0,
    m_TextureType = TextureType::TEXTURE_2D,
    m_Width = 0, m_Height = 0, m_Channels = 0,
    m_InternalFormat = GL_RGBA, m_Format = GL_RGBA,
    m_DataType = GL_UNSIGNED_BYTE,
    m_MinFilter = params.minFilter;
    m_MagFilter = params.magFilter,
    m_WrapS = params.wrapS,
    m_WrapT = params.wrapT,
    m_WrapR = params.wrapS,
    m_MipmapsGenerated = params.mipmapGenerated;

    stbi_set_flip_vertically_on_load(true);
    ImageParam imageParam = ResourceLoadUtils::getInstance().loadImage(texturePath);
    m_Width = imageParam.width;
    m_Height = imageParam.height;
    m_Channels = imageParam.channels;
    determineFormat(m_Channels);

    glGenTextures(1, &m_TextureId);
    glActiveTexture(GL_TEXTURE0); // 默认激活0号纹理单元
    glBindTexture(GL_TEXTURE_2D, m_TextureId);
    glTexImage2D(GL_TEXTURE_2D, 0, m_InternalFormat, m_Width, m_Height, 0,
                    m_Format, m_DataType, imageParam.data);

    if (m_MipmapsGenerated) {
        generateMipmaps();
    }

    setTextureParameters();

    imageParam.release();
}

Texture::Texture(const std::string &texturePath, const TextureParams &params, int textureUnit) {
    m_TextureId = 0,
    m_TextureUnit = textureUnit,
    m_TextureType = TextureType::TEXTURE_2D,
    m_Width = 0, m_Height = 0, m_Channels = 0,
    m_InternalFormat = GL_RGBA, m_Format = GL_RGBA,
    m_DataType = GL_UNSIGNED_BYTE,
    m_MinFilter = params.minFilter;
    m_MagFilter = params.magFilter,
    m_WrapS = params.wrapS,
    m_WrapT = params.wrapT,
    m_WrapR = params.wrapS,
    m_MipmapsGenerated = params.mipmapGenerated;

    stbi_set_flip_vertically_on_load(true);
    ImageParam imageParam = ResourceLoadUtils::getInstance().loadImage(texturePath);
    m_Width = imageParam.width;
    m_Height = imageParam.height;
    m_Channels = imageParam.channels;
    determineFormat(m_Channels);

    glGenTextures(1, &m_TextureId);
    glActiveTexture(GL_TEXTURE0 + m_TextureUnit);
    glBindTexture(GL_TEXTURE_2D, m_TextureId);
    glTexImage2D(GL_TEXTURE_2D, 0, m_InternalFormat, m_Width, m_Height, 0,
                    m_Format, m_DataType, imageParam.data);

    if (m_MipmapsGenerated) {
        generateMipmaps();
    }

    setTextureParameters();

    imageParam.release();
}


Texture::Texture(const std::string &texturePath, const TextureParams &params, int textureUnit, bool enableFlip) {
    m_TextureId = 0,
    m_TextureUnit = textureUnit,
    m_TextureType = TextureType::TEXTURE_2D,
    m_Width = 0, m_Height = 0, m_Channels = 0,
    m_InternalFormat = GL_RGBA, m_Format = GL_RGBA,
    m_DataType = GL_UNSIGNED_BYTE,
    m_MinFilter = params.minFilter;
    m_MagFilter = params.magFilter,
    m_WrapS = params.wrapS,
    m_WrapT = params.wrapT,
    m_WrapR = params.wrapS,
    m_MipmapsGenerated = params.mipmapGenerated;

    stbi_set_flip_vertically_on_load(enableFlip);
    ImageParam imageParam = ResourceLoadUtils::getInstance().loadImage(texturePath);
    m_Width = imageParam.width;
    m_Height = imageParam.height;
    m_Channels = imageParam.channels;
    determineFormat(m_Channels);

    glGenTextures(1, &m_TextureId);
    glActiveTexture(GL_TEXTURE0); // 默认激活0号纹理单元
    glBindTexture(GL_TEXTURE_2D, m_TextureId);
    glTexImage2D(GL_TEXTURE_2D, 0, m_InternalFormat, m_Width, m_Height, 0,
                    m_Format, m_DataType, imageParam.data);

    if (m_MipmapsGenerated) {
        generateMipmaps();
    }

    setTextureParameters();

    imageParam.release();
}


void Texture::generateMipmaps() {
    bind();
    switch (m_TextureType) {
        case TextureType::TEXTURE_2D:
            glGenerateMipmap(GL_TEXTURE_2D);
            break;
        case TextureType::TEXTURE_CUBE_MAP:
            glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
            break;
        default:
            std::cerr << "Mipmap generation not supported for this texture type" << std::endl;
    }
    m_MipmapsGenerated = true;
    unbind();
}


void Texture::setFilter(FilterMode minFilter, FilterMode magFilter) {
    m_MinFilter = minFilter;
    m_MagFilter = magFilter;

    bind();
    glTexParameteri(getGLTextureType(), GL_TEXTURE_MIN_FILTER, static_cast<GLint>(minFilter));
    glTexParameteri(getGLTextureType(), GL_TEXTURE_MAG_FILTER, static_cast<GLint>(magFilter));
    unbind();
}

void Texture::setWrapper(WrapMode wrapS, WrapMode wrapT, WrapMode wrapR) {
    m_WrapS = wrapS;
    m_WrapT = wrapT;
    m_WrapR = wrapR;

    bind();
    glTexParameteri(getGLTextureType(), GL_TEXTURE_WRAP_S, static_cast<GLint>(wrapS));
    glTexParameteri(getGLTextureType(), GL_TEXTURE_WRAP_T, static_cast<GLint>(wrapT));
    if (m_TextureType == TextureType::TEXTURE_CUBE_MAP || m_TextureType == TextureType::TEXTURE_3D) {
        glTexParameteri(getGLTextureType(), GL_TEXTURE_WRAP_R, static_cast<GLint>(wrapR));
    }
    unbind();
}

void Texture::setSamplerName(const std::string &samplerName) {
    m_SamplerName = samplerName;
}


void Texture::bind() const {
    glActiveTexture(GL_TEXTURE0 + m_TextureUnit);
    switch (m_TextureType) {
        case TextureType::TEXTURE_2D:
            glBindTexture(GL_TEXTURE_2D, m_TextureId);
            break;
        case TextureType::TEXTURE_CUBE_MAP:
            glBindTexture(GL_TEXTURE_CUBE_MAP, m_TextureId);
            break;
        case TextureType::TEXTURE_2D_ARRAY:
            glBindTexture(GL_TEXTURE_2D_ARRAY, m_TextureId);
            break;
        case TextureType::TEXTURE_3D:
            glBindTexture(GL_TEXTURE_3D, m_TextureId);
            break;
        default:
            break;
    }
}

void Texture::unbind() const {
    switch (m_TextureType) {
        case TextureType::TEXTURE_2D:
            glBindTexture(GL_TEXTURE_2D, 0);
            break;
        case TextureType::TEXTURE_CUBE_MAP:
            glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
            break;
        case TextureType::TEXTURE_2D_ARRAY:
            glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
            break;
        case TextureType::TEXTURE_3D:
            glBindTexture(GL_TEXTURE_3D, 0);
            break;
        default:
            break;
    }
}

int Texture::getTextureUnit() const {
    return m_TextureUnit;
}

std::string Texture::getSamplerName() const {
    return m_SamplerName;
}